//
// File: network.cpp
// Description: 
//

#include "network.h"

Network *Network::network = 0;

//
//
Network & Network::run() {
	if (network != 0) return *network;
	
	
	network = new Network(DEF_PORT);
	if (network->mError != NO_ERROR) {
		return *network;
	}
	
	pthread_t tid;
	pthread_create(&tid, NULL, Network::network_thread, network);
	
	return *network;
}

//
// Constructor
Network::Network(int port) {
	address.host = ENET_HOST_ANY;
	address.port = port;
	
	
	if (enet_initialize() != 0) {
		mError = FAILED_TO_CREATE_HOST;
		return;
	}
	
	server = enet_host_create(&address, 32, 0, 0);
	if (server == NULL) {
		mError = FAILED_TO_CREATE_HOST;
	}
	
	mError = NO_ERROR;
}

//
// Deconstructor
Network::~Network() {
	enet_host_destroy(server);
	enet_deinitialize();
}

//
//
int Network::size() {
	return eventList.size();
}

//
//
int Network::error() {
	return mError;
}

//
//
bool Network::pollEvent(NetworkEvent &e) {
	if (size() == 0) return false;
	
	e = eventList.front();
	eventList.pop();
	
	return true;
}


//
//
void Network::listen() {
	cout << "Listening on " << address.port << endl;
	
	ENetEvent event;
	while(enet_host_service(server, &event, 1000) >= 0) {
		
		//NetworkEvent e;
		//e.peer.address = event.peer->address.host;
		//e.peer.port = event.peer->address.port;
		
		switch(event.type) {
			case ENET_EVENT_TYPE_CONNECT: {
				NetworkEvent e;
				e.type = NetworkEvent::CONNECT;
				e.peer.port = event.peer->address.port;
				e.peer.address = event.peer->address.host;
				
				eventList.push(e);
				
				break;
			}
			
			case ENET_EVENT_TYPE_RECEIVE: {
				NetworkEvent e;
				e.type = NetworkEvent::DATA;
				e.peer.port = event.peer->address.port;
				e.peer.address = event.peer->address.host;
				
				eventList.push(e);
				
				
				enet_packet_destroy(event.packet);
				
				break;
			}
			
			case ENET_EVENT_TYPE_DISCONNECT: {
				NetworkEvent e;
				e.type = NetworkEvent::DISCONNECT;
				e.peer.port = event.peer->address.port;
				e.peer.address = event.peer->address.host;
				
				eventList.push(e);
				
				break;
			}
			
		}
	}
}

//
//
void *Network::network_thread(void *param) {
	
	Network & n = *((Network*)param);
	n.listen();
	
	pthread_exit(NULL);
}

//
//
bool NetworkPeer::operator<(const NetworkPeer & other) const {
	return address == address ? port < other.port : address < other.address;
}

ostream & operator<<(ostream & os, const NetworkPeer & p) {
	os << (int)((char)(p.address)) << "." << (int)((char)(p.address >> 8)) << "." << (int)((char)(p.address >> 16)) << "." << (int)((char)(p.address >> 24));
	
	os << ":" << p.port;
	return os;
}
